Cristina Sierra
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import requests
import json
import time
import xgboost as xgb
from xgboost import XGBRegressor
from xgboost import plot_importance, plot_tree
from sklearn.metrics import mean_squared_error, mean_absolute_error
from sklearn.model_selection import train_test_split
from sklearn import datasets, linear_model
import datetime
from sklearn.calibration import CalibratedClassifierCV
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import RandomizedSearchCV, GridSearchCV
from sklearn.model_selection import cross_val_score
from sklearn.metrics import mean_squared_error
import warnings
warnings.filterwarnings("ignore")
Fuente: Kaggle, Organización Mundial de la Salud
Para el análisis del problema se utilizaron dos fuentes de información. Kaggle, del cual se obtuvieron dos datasets, uno con información de la propagación del virus durante dos meses, y otro con información poblacional de los países. Adicionalmente, de la OMS se obtuvieron datos asociados a condiciones de salud en los países.
df = pd.read_csv('C:/Users/Usuario/Downloads/corona-virus-report/covid_19_clean_complete.csv',parse_dates=['Date'])
df_pop = pd.read_csv('C:/Users/Usuario/Downloads/population_by_country_2020.csv')
df_oms = pd.read_csv('C:/Users/Usuario/Downloads/oms_info.csv')
print(f"Rows: {df.shape[0]}, Columns: {df.shape[1]}")
print(f"Rows: {df_pop.shape[0]}, Columns: {df_pop.shape[1]}")
print(f"Rows: {df_oms.shape[0]}, Columns: {df_oms.shape[1]}")
df.head()
df.describe().T
def missing_data(data):
total = data.isnull().sum()
percent = (data.isnull().sum()/data.isnull().count()*100)
tt = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])
types = []
for col in data.columns:
dtype = str(data[col].dtype)
types.append(dtype)
tt['Types'] = types
return(np.transpose(tt))
missing_data(df)
print(f"Countries/Regions:{df['Country/Region'].nunique()}")
print(f"Province/State:{df['Province/State'].nunique()}")
El tratamiento a datos faltantes en el caso de Province/State son imputados con la información de Country/State. Los demás datos se imputaron con el valor 0.
df['Province/State'] = df['Province/State'].fillna(df['Country/Region'])
df.fillna(0, inplace=True)
print(f"Dato más antiguo: {df['Date'].min()}")
print(f"Dato más reciente: {df['Date'].max()}")
print(f"Total días: {df['Date'].max() - df['Date'].min()}")
df_pop.head()
df_pop.describe().T
missing_data(df_pop)
df_oms.head()
df_oms = df_oms.groupby('Country').first().reset_index()
df_oms.shape
df_oms.describe().T
missing_data(df_oms)
df_oms.fillna(0, inplace = True)
missing_data(df_oms)
Cálculo de casos activos con base en los casos confirmados, muertes y recuperaciones
df['Active']=df['Confirmed']-df['Deaths']-df['Recovered']
Evolución de los casos posibles y los tres posibles estados del virus.
df_grouped = df.groupby('Date')['Date', 'Deaths', 'Recovered','Active','Confirmed'].sum().reset_index()
fig = make_subplots(rows=2, cols=2)
fig.add_trace(go.Scatter(x=df_grouped['Date'], y=df_grouped['Deaths'],
name="Deaths", line_color='red',
opacity=0.8), row=2, col=2)
fig.add_trace(go.Scatter(x=df_grouped['Date'], y=df_grouped['Recovered'],
name="Recovered", line_color='gray',
opacity=0.8), row=1, col=2)
fig.add_trace(go.Scatter( x=df_grouped['Date'], y=df_grouped['Active'],
name="Active", line_color='black',
opacity=0.8), row=2, col=1)
fig.add_trace(go.Scatter( x=df_grouped['Date'], y=df_grouped['Confirmed'],
name="Confirmed", line_color='blue',
opacity=0.8), row=1, col=1)
fig.update_layout(height=600, width=800, title_text="Coronavirus Evolution")
fig.show()
Se observa que todas las variables han mostrado un crecimiento exponencial. En particular los casos activos tuvieron una reducción en la mitad del periodo de estudio que está asociado a la recuperación de casos en China.
b = df[['Country/Region','Confirmed','Date']].groupby(['Country/Region']).sum().reset_index()
b.sort_values(by='Confirmed', ascending=False, inplace=True)
df_grouped_top = df[df['Country/Region'].isin(b['Country/Region'].head(10))].groupby(['Date', 'Country/Region'])['Date','Country/Region', 'Deaths', 'Recovered','Active','Confirmed'].sum().reset_index()
fig = px.line(df_grouped_top, x="Date", y="Confirmed", color='Country/Region',
title='Evolución casos confirmados para top 10 países')
fig.show()
Casos confirmados con transformación lograrítimica.
df_grouped_top['Confirmed_log']=df_grouped_top['Confirmed'].apply(lambda x:np.log(x))
fig = px.line(df_grouped_top, x="Date", y="Confirmed_log", color='Country/Region',
title='Evolución casos confirmados para top 10 países (log)')
fig.show()
Se observa como China es el único país en fase de recuperación, en el que los contagios ya no crecen de manera exponencial, a diferencia de los demás países en los que crece de forma vetiginosa los contagios cada día.
b.sort_values(by='Confirmed', ascending=False, inplace=True)
b.head(10)
df['Confirmed_log']=df['Confirmed'].apply(lambda x:np.log(x))
china_grouped = df[df['Country/Region'] == 'China'].reset_index()
china_grouped_ = china_grouped.groupby('Date')['Date', 'Deaths', 'Recovered','Active','Confirmed','Confirmed_log'].sum().reset_index()
italy_grouped = df[df['Country/Region'] == 'Italy'].reset_index()
italy_grouped_ = italy_grouped.groupby('Date')['Date', 'Deaths', 'Recovered','Active','Confirmed','Confirmed_log'].sum().reset_index()
iran_grouped = df[df['Country/Region'] == 'Iran'].reset_index()
iran_grouped_ = iran_grouped.groupby('Date')['Date', 'Deaths', 'Recovered','Active','Confirmed','Confirmed_log'].sum().reset_index()
spain_grouped = df[df['Country/Region'] == 'Spain'].reset_index()
spain_grouped_ = spain_grouped.groupby('Date')['Date', 'Deaths', 'Recovered','Active','Confirmed','Confirmed_log'].sum().reset_index()
us_grouped = df[df['Country/Region'] == 'US'].reset_index()
us_grouped_ = us_grouped.groupby('Date')['Date', 'Deaths', 'Recovered','Active','Confirmed','Confirmed_log'].sum().reset_index()
fig = px.line(china_grouped_, x="Date", y="Confirmed_log", title='China')
fig.show()
fig = px.line(italy_grouped_, x="Date", y="Confirmed_log", title='Italy')
fig.show()
fig = px.line(iran_grouped_, x="Date", y="Confirmed_log", title='Iran')
fig.show()
fig = px.line(spain_grouped_, x="Date", y="Confirmed_log", title='Spain')
fig.show()
fig = px.line(us_grouped_, x="Date", y="Confirmed_log", title='US')
fig.show()
df_temp = df[[col for col in df.columns if col != 'Province/State']]
recent = df_temp[df_temp['Date'] == max(df_temp['Date'])].reset_index()
recent_grouped = recent.groupby('Country/Region')['Confirmed', 'Deaths', 'Active', 'Recovered'].sum().reset_index()
Así se ven los países teniendo en cuenta la saturación de casos confirmados:
fig = px.choropleth(recent_grouped, locations="Country/Region",
locationmode='country names', color="Confirmed",
hover_name="Country/Region", range_color=[1,5000],
color_continuous_scale="peach",
title='Países con casos confirmados')
fig.show()
En el caso particular de Europa, así es la situación:
fig = px.choropleth(recent_grouped, locations="Country/Region",
locationmode='country names', color="Confirmed",
hover_name="Country/Region", range_color=[1,5000],
color_continuous_scale="peach", scope='europe',
title='Países con casos confirmados Europa')
# fig.update(layout_coloraxis_showscale=False)
fig.show()
fig = px.bar(recent_grouped.sort_values('Confirmed', ascending=False)[:20][::-1],
x='Confirmed', y='Country/Region',
title='Casos confirmados', text='Confirmed', height=1000, orientation='h')
fig.show()
Si bien es cierto, los países más afectados son China e Italia en cuanto a contagios (hasta la fecha). En términos de tasa de muertes y tasa de recuperación los países con peores patrones son los siguientes:
cleaned_recent = df[df['Date'] == max(df['Date'])]
flg = cleaned_recent.groupby('Country/Region')['Confirmed', 'Deaths', 'Recovered', 'Active'].sum().reset_index()
flg['mortalityRate'] = round((flg['Deaths']/flg['Confirmed'])*100, 2)
temp = flg[flg['Confirmed']>100]
temp = temp.sort_values('mortalityRate', ascending=False)
fig = px.bar(temp.sort_values(by="mortalityRate", ascending=False)[:10][::-1],
x = 'mortalityRate', y = 'Country/Region',
title='Muertes por cada 100 casos confirmados', text='mortalityRate', height=800, orientation='h',
color_discrete_sequence=['darkred']
)
fig.show()
flg['recoveryRate'] = round((flg['Recovered']/flg['Confirmed'])*100, 2)
df_temp = flg[flg['Confirmed']>100]
df_temp = df_temp.sort_values('recoveryRate', ascending=False)
fig = px.bar(df_temp.sort_values(by="recoveryRate", ascending=False)[:10][::-1],
x = 'recoveryRate', y = 'Country/Region',
title='Recuperaciones por cada 100 casos confirmados', text='recoveryRate', height=800, orientation='h',
color_discrete_sequence=['#2ca02c']
)
fig.show()
print("Países con peores tasas de recuperación")
df_temp = flg[flg['Confirmed']>100]
df_temp = df_temp.sort_values('recoveryRate', ascending=True)[['Country/Region', 'Confirmed','Recovered']][:20]
df_temp.sort_values('Confirmed', ascending=False)[['Country/Region', 'Confirmed','Recovered']][:20]
Estados Unidos, llama la atención porque tiene la peor tasa de recuperación. Así es la distribución de casos confirmados entre estados en este país:
df_usa = df[df['Country/Region'] == "US"]
usa_recent = df_usa[df_usa['Date'] == max(df_usa['Date'])]
usa_recent = usa_recent.groupby('Province/State')['Confirmed', 'Deaths'].max().reset_index()
fig = px.bar(usa_recent.sort_values('Confirmed', ascending=False)[:10][::-1],
x='Confirmed', y='Province/State', color_discrete_sequence=['#D63230'],
title='Confirmed Cases in USA', text='Confirmed', orientation='h')
fig.show()
Ahora bien, vale la pena hacer énfasis en Colombia, país que presentó el primer caso de contagio el día 6 de marzo y de allí en adelante el aumento de casos tuvo un alto crecimiento llegando a 230 dos semanas después. Al día 23 de marzo (último día con datos recientes) solo se había presentado dos muertes en el país debido al coronavirus, lo cual indica una tasa de fatalidad baja. Sin embargo, la tasa de recuperación se mantiene igualmente baja, manteniéndose una alta cantidad de casos activos lo cual hace más propensa la propagación exponencial del virus.
co_grouped = df[df['Country/Region'] == 'Colombia'].reset_index()
co_grouped_ = co_grouped.groupby('Date')['Date', 'Deaths', 'Recovered','Active','Confirmed','Confirmed_log'].sum().reset_index()
fig = px.line(co_grouped_, x="Date", y="Confirmed", title='Colombia')
fig.show()
fig = px.line(co_grouped_, x="Date", y="Confirmed_log", title='Colombia')
fig.show()
Finalmente, la propagación del contagio en el planeta a lo largo de los días de estudio se puede visualizar a continuación:
formated_gdf = df.groupby(['Date', 'Country/Region'])['Confirmed', 'Deaths'].max()
formated_gdf = formated_gdf.reset_index()
formated_gdf['Date'] = pd.to_datetime(formated_gdf['Date'])
formated_gdf['Date'] = formated_gdf['Date'].dt.strftime('%m/%d/%Y')
formated_gdf['size'] = formated_gdf['Confirmed'].pow(0.3)
fig = px.scatter_geo(formated_gdf, locations="Country/Region", locationmode='country names',
color="Confirmed", size='size', hover_name="Country/Region",
range_color= [0, 1500],
projection="natural earth", animation_frame="Date",
title='Coronavirus: propagación en el tiempo', color_continuous_scale="portland")
# fig.update(layout_coloraxis_showscale=False)
fig.show()
A continuación se utiliza la información obtenida para analizar las variables que mejor explican la propagación del virus.
df_model = df.merge(df_pop, left_on='Country/Region', right_on='Country (or dependency)')
print(df_model.shape)
df_model = df_model.merge(df_oms, left_on='Country/Region', right_on = 'Country')
print(df_model.shape)
df_model.head()
Se calculan variables asociadas al tiempo como día del mes, mes, y día del año equivalente.
df_model['Day'] = df_model['Date'].apply(lambda x:x.day)
df_model['Month'] = df_model['Date'].apply(lambda x:x.month)
df_model['Day_year'] = df_model['Date'].apply(lambda x:x.strftime('%j'))
df_model['Day_year'] = df_model['Day_year'].astype(int)
La variable a explicar es el logaritmo de los casos confirmado del virus.
df_model['Confirmed_log']=df_model['Confirmed'].apply(lambda x: np.log(1+x))
df_model.replace([np.inf, -np.inf], 0, inplace=True)
df_model['Confirmed_log'].describe()
df_model.columns
X = df_model.drop(columns=['Date', 'Confirmed', 'Deaths', 'Recovered',
'Active', 'Country (or dependency)', 'Migrants (net)', 'Confirmed_log',
'Yearly Change', 'Country', 'Year', 'World Share', 'Urban Pop %',
'Fert. Rate', 'Med. Age'])
y = df_model['Confirmed_log']
X = pd.concat([X,pd.get_dummies(X['Province/State'], prefix='ps')],axis=1)
X.drop(['Province/State'],axis=1, inplace=True)
X = pd.concat([X,pd.get_dummies(X['Country/Region'], prefix='cr')],axis=1)
X.drop(['Country/Region'],axis=1, inplace=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
print (X_train.shape, y_train.shape)
print (X_test.shape, y_test.shape)
xgb = XGBRegressor(n_estimators=100)
model1 = xgb.fit(X_train, y_train)
cross_val_score(model1, X_train, y_train, cv=10, scoring='neg_mean_squared_error').mean()
cross_val_score(model1, X_test, y_test, cv=10, scoring='neg_mean_squared_error').mean()
plot = plot_importance(model1, height=0.9, max_num_features=20)
plot
Las variables con mayor efecto sobre la propagación claramente es el paso del de los días, adicionalmente el volumen de problación del país y la densidad puesto que hace el contagio más probable. También la latitud y longitug tienen una participación importante del problema, de manera que estar ubicados con menor distancia a los primeros lugares de contagio aumenta la posibilidad de contagio.
predictions = model1.predict(X_test)
print('Mean squared error:',mean_squared_error(y_test, predictions))
pd.DataFrame(predictions,y_test).head(15)
Para finalizar, procedo a guardar la base de datos utilizada. Sin embargo, no suelo hacer uso de bases externas de datos, sino siempre privadas por lo que dejo comentado el proceso y la forma en la que guardo y leo bases de datos. Creo que podría hacer una prueba específica de sql porque igual el procesamiento aquí se hizo directamente en python antes de poder guardarlo en una base de datos.
Conexion base de datos snowflake
snowflake_configuration = {
'url' : 'url.com',
'account' : '',
'user' : '',
'port' : ,
'warehouse': '',
'password' : '',
'database' : ''
}
engine = create_engine(URL(**snowflake_configuration))
connection = engine.connect()
con = snowflake.connector.connect(**snowflake_configuration)
connection.execute(f"USE SCHEMA base_prueba;")
connection.execute(f"USE WAREHOUSE NORMAL_LOAD;")
Guardar base de datos
table_name='covid_table'
schem='base_prueba'
base.to_sql(table_name, engine, if_exists='replace',schema=schem, index=False, chunksize=16383)
connection.execute(f"grant select on table base_prueba.covid_table to role public;")
Lectura base de datos
snowflake_data = '''
select country/region, province/state,
case when country/region = 'China' then 'China' else 'Rest of the world' end as China,
sum(confirmed) as confirmados, sum(recovered) as recuperados , sum(active) as activos,
sum(deaths) as muertes, recuperados/confirmados as ts_recuperacion, muertes/confirmados
as ts_muertes
from base_prueba.covid_table
group by 1,2,3 order by activos
'''
df_snowflake = pd.read_sql(snowflake_data, connection)